package service

import (
	"context"
	"encoding/json"
	redis2 "github.com/redis/go-redis/v9"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.uber.org/zap"
	v1 "golang-sgg-daijia/api/v1"
	"golang-sgg-daijia/internal/common/util"
	"golang-sgg-daijia/internal/const/orderconst"
	"golang-sgg-daijia/internal/const/redisconst"
	"golang-sgg-daijia/internal/model"
	"golang-sgg-daijia/internal/repository/mongodb"
	"golang-sgg-daijia/internal/repository/mysql"
	"golang-sgg-daijia/internal/repository/redis"
	"strconv"
	"time"
)

type LocationService interface {
	UpdateDriverLocation(ctx context.Context, req *v1.UpdateDriverLocationForm) (bool, error)
	RemoveDriverLocation(ctx context.Context, driverID int64) (bool, error)
	SearchNearByDriver(ctx context.Context, form *v1.SearchNearByDriverForm) (
		[]*v1.NearByDriverVo, error)
	UpdateOrderLocationToCache(ctx context.Context, updateOrderLocForm *v1.UpdateOrderLocationForm) (bool, error)
	GetCacheOrderLocation(ctx context.Context, orderID int64) (*v1.OrderLocationVo, error)
	SaveOrderServiceLocation(ctx context.Context, req []*v1.SaveOrderServiceLocationForm) (bool, error)
	GetOrderServiceLastLocation(ctx context.Context, orderID int64) (*v1.OrderServiceLastLocationVo, error)
	CalculateOrderRealDistance(ctx context.Context, orderID int64) (float64, error)
}

func NewLocationService(
	service *Service,
	redisCli redis.RedisClient,
	driverSetDao mysql.DriverSetDao,
	orderServiceLocationDao mongodb.OrderServiceLocationDao,
	orderInfoDao    mysql.OrderInfoDao,
) LocationService {
	return &locationService{
		Service:                 service,
		rdb:                     redisCli,
		driverSetDao:            driverSetDao,
		orderServiceLocationDao: orderServiceLocationDao,
		orderInfoDao:    orderInfoDao,
	}
}

type locationService struct {
	*Service
	rdb                     redis.RedisClient
	driverSetDao            mysql.DriverSetDao
	orderServiceLocationDao mongodb.OrderServiceLocationDao
	orderInfoDao    mysql.OrderInfoDao
}

func (l *locationService) CalculateOrderRealDistance(ctx context.Context, orderID int64) (float64, error) {
	// 1 根据订单id获取代价订单位置信息，根据创建时间排序（升序）
	// 查询mongodb
	list, err := l.orderServiceLocationDao.FindByOrderIdOrderByCreateTimeAsc(ctx, orderID)
	if err != nil {
		return 0, err
	}
	// 2 第一步查询返回订单位置信息list集合
	// 遍历集合，计算两个位置距离
	// 把计算所有距离相加操作
	realDistance := 0.0
	for i := 0; i < len(list)-1; i++ {
		distance := util.GetDistance(list[i].Latitude, list[i].Longitude, list[i+1].Latitude, list[i+1].Longitude)
		realDistance += distance
	}
	// todo 为了测试，不好测试实际代驾距离，模拟数据  实际距离=预估距离+5公里
	if realDistance == 0 {
		orderInfo, err := l.orderInfoDao.GetByID(ctx, int(orderID))
		if err != nil {
			return 0, err
		}
		if orderInfo != nil {
			return orderInfo.ExpectDistance+5, nil
		}
		return 10, nil
	}

	return realDistance, nil
}

func (l *locationService) GetOrderServiceLastLocation(ctx context.Context, orderID int64) (*v1.OrderServiceLastLocationVo, error) {
	// 查询MongoDB
	// 查询条件： orderID
	// 根据时间降序排序
	// 最新一条数据
	orderSerLoc, err := l.orderServiceLocationDao.GetNewestByOrderID(ctx, orderID)
	if err != nil {
		return nil, err
	}
	return &v1.OrderServiceLastLocationVo{
		Latitude:  orderSerLoc.Latitude,
		Longitude: orderSerLoc.Longitude,
	}, nil
}

func (l *locationService) SaveOrderServiceLocation(ctx context.Context, req []*v1.SaveOrderServiceLocationForm) (bool, error) {
	list := make([]interface{}, len(req), len(req))
	for i, saveOrderSerLocForm := range req {
		orderSerLoc := &model.OrderServiceLocation{
			ID:         primitive.NewObjectID().String(),
			OrderID:    saveOrderSerLocForm.OrderID,
			Latitude:   saveOrderSerLocForm.Latitude,
			Longitude:  saveOrderSerLocForm.Longitude,
			CreateTime: time.Now(),
		}
		list[i] = orderSerLoc
	}
	flag, err := l.orderServiceLocationDao.InsertMany(ctx, list)
	if err != nil {
		return false, err
	}
	return flag, nil
}

func (l *locationService) GetCacheOrderLocation(ctx context.Context, orderID int64) (*v1.OrderLocationVo, error) {
	orderLocStr, err := l.rdb.Get(ctx, redisconst.UpdateOrderLocation+strconv.FormatInt(orderID, 10))
	if err != nil {
		return nil, err
	}
	orderLocVo := &v1.OrderLocationVo{}
	err = json.Unmarshal([]byte(orderLocStr), orderLocVo)
	if err != nil {
		l.logger.WithContext(ctx).Error("locationService|GetCacheOrderLocation|Unmarshal", zap.Any("data", map[string]interface{}{
			"orderLocStr": orderLocStr,
		}), zap.Error(err))
		return nil, err
	}
	return orderLocVo, nil
}

func (l *locationService) UpdateOrderLocationToCache(ctx context.Context, updateOrderLocForm *v1.UpdateOrderLocationForm) (bool, error) {
	orderLocationVo := &v1.OrderLocationVo{
		Latitude:  updateOrderLocForm.Latitude,
		Longitude: updateOrderLocForm.Longitude,
	}
	key := redisconst.UpdateOrderLocation + strconv.FormatInt(updateOrderLocForm.OrderID, 10)
	val, err := json.Marshal(orderLocationVo)
	if err != nil {
		l.logger.WithContext(ctx).Error("locationService|UpdateOrderLocationToCache|Marshal", zap.Any("data", map[string]interface{}{
			"orderLocVo": orderLocationVo,
		}), zap.Error(err))
		return false, err
	}
	err = l.rdb.Set(ctx, key, val, time.Hour*24)
	if err != nil {
		return false, err
	}
	return true, nil
}

// UpdateDriverLocation 更新司机位置信息
func (l *locationService) UpdateDriverLocation(ctx context.Context, req *v1.UpdateDriverLocationForm) (bool, error) {
	driverSet, err := l.driverSetDao.GetByDriverID(ctx, req.DriverID)
	if err != nil {
		return false, err
	}
	if driverSet.ServiceStatus == 1 {
		return l.rdb.GeoAdd(ctx, redisconst.DriverGeoLocation, &redis2.GeoLocation{
			Name:      strconv.FormatInt(req.DriverID, 10),
			Latitude:  req.Latitude,
			Longitude: req.Longitude,
		})
	}
	return false, nil
}

// RemoveDriverLocation 删除司机位置信息
func (l *locationService) RemoveDriverLocation(ctx context.Context, driverID int64) (bool, error) {
	return l.rdb.GeoDel(ctx, redisconst.DriverGeoLocation, strconv.FormatInt(driverID, 10))
}

// SearchNearByDriver 搜索附近满足条件的司机
func (l *locationService) SearchNearByDriver(ctx context.Context, form *v1.SearchNearByDriverForm) (
	[]*v1.NearByDriverVo, error) {

	//搜索经纬度位置5公里以内的司机
	geoLocations, err := l.rdb.GeoRadius(ctx, redisconst.DriverGeoLocation, form.Longitude, form.Latitude, &redis2.GeoRadiusQuery{
		Radius:    orderconst.AcceptDistance,
		Unit:      "km",
		WithCoord: true,  // 包含坐标
		WithDist:  true,  // 包含距离
		Sort:      "ASC", // 升序
	})
	if err != nil {
		return nil, err
	}
	var list []*v1.NearByDriverVo
	for _, geoLocation := range geoLocations {
		driverID, _ := strconv.ParseInt(geoLocation.Name, 10, 64) // 忽略错误
		// 根据司机id个性化设置信息
		driverSet, err := l.driverSetDao.GetByDriverID(ctx, driverID)
		if err != nil {
			continue
		}
		// 判断订单里程order_distance
		//如果不等于0 ，比如30，接单30公里代驾订单。
		//接单距离 - 当前单子距离  < 0,不复合条件
		if driverSet.OrderDistance != 0 && driverSet.OrderDistance-form.MileageDistance < 0 {
			continue
		}
		//判断接单里程 accept_distance
		if driverSet.AcceptDistance != 0 && driverSet.AcceptDistance-geoLocation.Dist < 0 {
			continue
		}
		list = append(list, &v1.NearByDriverVo{
			DriverID: driverID,
			Distance: geoLocation.Dist,
		})
	}
	return list, nil
}
